package com.zn.opit.shirodemo.controller;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.zn.opit.shirodemo.domain.R;
import com.zn.opit.shirodemo.domain.consts.CommonConst;
import com.zn.opit.shirodemo.domain.entity.SysPermEntity;
import com.zn.opit.shirodemo.domain.entity.SysUserEntity;
import com.zn.opit.shirodemo.domain.enums.SysPermType;
import com.zn.opit.shirodemo.domain.po.SysRolePO;
import com.zn.opit.shirodemo.domain.req.LoginReq;
import com.zn.opit.shirodemo.domain.resp.LoginUserResp;
import com.zn.opit.shirodemo.exception.ShiroDemoException;
import com.zn.opit.shirodemo.exception.ShiroDemoExceptionTypes;
import com.zn.opit.shirodemo.service.SysRoleService;
import com.zn.opit.shirodemo.service.SysUserService;
import com.zn.opit.shirodemo.utils.RedisUtil;
import io.swagger.annotations.ApiOperation;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.ExcessiveAttemptsException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.crypto.hash.Sha256Hash;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpSession;
import java.util.ArrayList;
import java.util.List;

@RestController
@RequestMapping("auth")
public class AuthController {

    protected final String USER_KEY = "user-key";

    @Autowired
    private SysUserService userService;
    @Autowired
    private SysRoleService roleService;
    @Resource
    private RedisUtil redisUtil;

    @ApiOperation(value = "未登陆")
    @GetMapping("/noLogin")
    public R noLogin() {
        return R.error(ShiroDemoExceptionTypes.NO_LOGIN);
    }

    @ApiOperation(value = "登陆认证")
    @PostMapping("/login")
    public R login(@RequestBody LoginReq loginReq, HttpSession session) {
        Subject subject = SecurityUtils.getSubject();
        HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
        credentialsMatcher.setHashAlgorithmName(Sha256Hash.ALGORITHM_NAME);
        credentialsMatcher.setHashIterations(2);
        UsernamePasswordToken token = new UsernamePasswordToken(loginReq.getUsername(), loginReq.getPassword());
        try {
            subject.login(token);
        } catch (LockedAccountException lae) {
            throw new ShiroDemoException(ShiroDemoExceptionTypes.LOGIN_USER_BE_LOCKED);
        } catch (ExcessiveAttemptsException eae) {
            throw new ShiroDemoException(ShiroDemoExceptionTypes.LOGIN_USER_ATTEMPTS_TOO_MUCH);
        } catch (AuthenticationException e) {
            throw new ShiroDemoException(ShiroDemoExceptionTypes.LOGIN_USER_CREDENTIALS_FAILED);
        }
        if (subject.isAuthenticated()) {
            SysUserEntity dbUser = userService.getOne(new LambdaQueryWrapper<SysUserEntity>()
                    .eq(SysUserEntity::getUserId, subject.getPrincipal()));
            session.setAttribute(USER_KEY, dbUser);
            loginSuccessInit(dbUser.getUserId());
            return R.ok("登录成功");
        } else {
            token.clear();
            throw new ShiroDemoException(ShiroDemoExceptionTypes.NO_LOGIN);
        }
    }

    /**
     * 登陆成功后一些初始化信息: 生成菜单、角色、权限放入ehCache
     *
     * @param username 用户名
     */
    private void loginSuccessInit(String username) {
        List<SysRolePO> roles = roleService.getRolesByUsername(username);
        redisUtil.set(CommonConst.LOGIN_USER_CACHE_KEY + username, roles);
    }

    @ApiOperation(value = "登出")
    @PostMapping("/logout")
    public R logout() {
        Subject subject = SecurityUtils.getSubject();
        if (subject != null) {
            subject.logout();
        }
        return R.ok("安全退出成功");
    }

    @ApiOperation(value = "获取当前登录用户信息")
    @GetMapping("/currentUserInfo")
    public R getCurrentUserInfo(HttpSession session) {
        Subject subject = SecurityUtils.getSubject();
        LoginUserResp loginUserResp = new LoginUserResp();
        SysUserEntity user = (SysUserEntity) session.getAttribute("user-key");
        BeanUtils.copyProperties(user, loginUserResp);
        loginUserResp.setUsername(user.getUserId());
        loginUserResp.setName(user.getNameCn());
        List<SysRolePO> SysRoleList = roleService.getRolesByUsername((String) subject.getPrincipal());
        List<String> roles = new ArrayList<>();
        List<String> perms = new ArrayList<>();
        List<SysPermEntity> menus = new ArrayList<>();
        SysRoleList.forEach(role -> {
            roles.add(role.getRoleId());
            role.getPerms().forEach(perm -> {
                perms.add(perm.getPermId());
                if (SysPermType.MENU.getCode().equals(perm.getType())) {
                    menus.add(perm);
                }
            });
        });
        loginUserResp.setRoles(roles);
        loginUserResp.setPerms(perms);
        loginUserResp.setMenus(menus);
        return R.ok().setData(loginUserResp);
    }
}
